<template>
	<view :id="id"></view>
</template>

<script>
	// 扩展 Quill.js 编辑器
	// #ifdef VUE3
	import '@/uni_modules/uni-cms/common/quill.min.js'
	import '@/uni_modules/uni-cms/common/quill-image-resize.js'
	// #endif
	// #ifndef VUE3
	import Quill from '@/uni_modules/uni-cms/common/quill.min.js'
	import QuillImageResize from '@/uni_modules/uni-cms/common/quill-image-resize.js'

	window.Quill = Quill
	window.ImageResize = QuillImageResize
	// #endif
	import {
		register as formatRegister
	} from './formats'
	import {
		register as moduleRegister
	} from './modules'
	import link from '../web/formats/link'

	export default {
		name: "editor", // 组件名
		props: {
			id: { // 编辑器的 id
				type: String,
				default: 'editor'
			},
			uploadHandler: { // 上传处理函数
				type: Function,
				default: null
			}
		},
		mounted() {
			// 初始化编辑器
			this.initEditor()
		},
		methods: {
			initEditor() {
				// 注册自定义格式
				formatRegister(Quill)
				// 注册自定义模块
				moduleRegister(Quill)

				// 注册图片缩放模块
				// #ifdef VUE3
				Quill.register('modules/ImageResize', window.ImageResize.default)
				// #endif
				// #ifndef VUE3
				Quill.register('modules/ImageResize', window.ImageResize)
				// #endif

				// 配置编辑器选项
				const options = {
					toolbar: false, // 隐藏工具栏
					readOnly: false, // 可编辑
					debug: false, // 关闭调试模式
					placeholder: '输入内容', // 占位符
					scrollingContainer: 'html,body', // 滚动容器
					scrollingConfig: {
						boundHeight: window.innerHeight - 100
					},
					modules: {
						MediaVideo: true,
						// 图片扩展模块
						ImageExtend: {
							uploadHandler: async (e, el) => {
								if (typeof this.uploadHandler !== 'function') {
									return uni.showModal({
										title: "请先配置上传方法",
										showCancel: false
									})
								}

								return this.uploadHandler(e, el)
							}
						},
						// 图片缩放模块
						ImageResize: {
							modules: ['DisplaySize', 'Toolbar', 'Resize']
						}
					}
				}

				// 创建编辑器实例
				this.quill = new Quill('#' + this.id, options)

				// 添加 Quill 事件监听器
				this.addQuillListeners()

				// 获取编辑器上下文
				const context = this.getEditorContext()

				// 触发 ready 事件并传递编辑器上下文
				this.$emit('ready', context)
			},

			getEditorContext() {
				// 返回一个对象，包含了编辑器的各种方法
				return {
					// 绑定 this，返回 format 方法
					format: this.format.bind(this),
					// 绑定 this，返回 insertDivider 方法
					insertDivider: this.insertDivider.bind(this),
					// 绑定 this，返回 insertImage 方法
					insertImage: this.insertImage.bind(this),
					// 绑定 this，返回 insertText 方法
					insertText: this.insertText.bind(this),
					// 绑定 this，返回 setContents 方法
					setContents: this.setContents.bind(this),
					// 绑定 this，返回 getContents 方法
					getContents: this.getContents.bind(this),
					// 绑定 this，返回 clear 方法
					clear: this.clear.bind(this),
					// 绑定 this，返回 removeFormat 方法
					removeFormat: this.removeFormat.bind(this),
					// 绑定 this，返回 undo 方法
					undo: this.undo.bind(this),
					// 绑定 this，返回 redo 方法
					redo: this.redo.bind(this),
					// 绑定 this，返回 blur 方法
					blur: this.blur.bind(this),
					// 绑定 this，返回 scrollIntoView 方法
					scrollIntoView: this.scrollIntoView.bind(this),
					// 绑定 this，返回 getSelectionText 方法
					getSelectionText: this.getSelectionText.bind(this),
					// 绑定 this，返回 insertUnlockContent 方法
					insertUnlockContent: this.insertUnlockContent.bind(this),
					// 绑定 this，返回 insertMediaVideo 方法
					insertMediaVideo: this.insertMediaVideo.bind(this),
				}
			},

			// 格式化文本
			format(name, _value = false) {
				// 获取当前光标所在位置
				const range = this.quill.getSelection(true)
				// 获取选中文本的值
				let value = _value
				// 获取选中文本的格式
				let format = this.quill.getFormat(range)[name] || false
				let isFormat = false
				// 如果是加粗、斜体、下划线、删除线、插入线
				if (['bold', 'italic', 'underline', 'strike', 'ins'].includes(name)) {
					// 取反
					value = !format
				} else if (name === 'direction') { // 如果是文本方向
					// 如果 value 是 rtl 且 format 存在，则 value 为 false，否则为 value
					value = value === 'rtl' && format ? false : value
					// 获取对齐方式
					const align = this.quill.getFormat(range).align
					// 如果 value 是 rtl 且 align 不存在
					if (value === 'rtl' && !align) {
						// 设置对齐方式为右对齐
						this.quill.format('align', 'right', Quill.sources.USER)
						isFormat = true
					} else if (!value && align === 'right') { // 如果 value 不存在且 align 是右对齐
						// 取消右对齐
						this.quill.format('align', false, Quill.sources.USER)
						isFormat = true
					}
				} else if (name === 'indent') { // 如果是缩进
					// 获取文本方向是否为 rtl
					const rtl = this.quill.getFormat(range).direction === 'rtl'
					// 如果 value 是 +1
					value = value === '+1'
					// 如果文本方向是 rtl
					if (rtl) {
						// 取反
						value = !value
					}
					// 如果 value 为 true，value 为 +1，否则为 -1
					value = value ? '+1' : '-1'
				} else if (name === 'link') {
					const [blot, offset] = this.quill.scroll.descendant(link(Quill)['formats/link'], range.index);

					if (blot) {
						this.quill.formatText({
							index: range.index - offset,
							length: blot.length()
						}, name, value, Quill.sources.USER)
					} else {
						this.quill.format(name, value, Quill.sources.USER)
					}
					isFormat = true
					//
					// if (range.length > 0) {
					//   this.quill.format(name, value, Quill.sources.USER)
					// } else {
					//
					//
					//   const [blot] = this.quill.getLeaf(range.index);
					//   if (blot && blot.domNode && blot.domNode.tagName === "A") {
					//     blot.parent.format(name, value)
					//     isFormat = true
					//   }
					// }
				} else { // 其他情况
					// 如果是列表
					if (name === 'list') {
						// 如果 value 是 check，value 为 unchecked，否则为 value
						value = value === 'check' ? 'unchecked' : value
						// 如果 format 是 checked，format 为 unchecked，否则为 format
						format = format === 'checked' ? 'unchecked' : format
					}
					// 如果 format 存在且不等于 value 或者 format 不存在且 value 存在，则 value 为 value，否则为 format 的取反
					value = ((format && format !== (value || false)) || (!format && value)) ? value : !format
				}
				// 设置格式
				!isFormat && this.quill.format(name, value, Quill.sources.USER)

				// 触发状态改变事件
				this.emitStatusChange(range)
			},

			insertDivider() {
				// 获取当前光标所在位置
				const range = this.quill.getSelection(true)
				// 在当前光标位置插入一个换行符
				this.quill.insertText(range.index, '\n', Quill.sources.USER)
				// 在当前光标位置插入一个分割线
				this.quill.insertEmbed(range.index + 1, 'divider', true, Quill.sources.USER)
				// 将光标移动到分割线下一行
				this.quill.setSelection(range.index + 2, Quill.sources.SILENT)
			},

			insertImage({
				src,
				alt,
				width,
				height,
				extClass,
				data = {}
			} = {}) {
				// 获取当前光标所在位置
				const range = this.quill.getSelection(true)

				// 如果当前光标前面不是换行符，则在当前光标位置插入一个换行符
				if (range.index > 0 && this.quill.getText(range.index - 1, 1) !== '\n') {
					this.quill.insertText(range.index, '\n', Quill.sources.SILENT)
					range.index += 1;
				}

				// 在当前光标位置插入一个图片
				this.quill.insertEmbed(range.index, 'image', {
					src,
					uploaded: true
				}, Quill.sources.SILENT)
				// 设置图片的本地路径
				this.quill.formatText(range.index, 1, 'data-local', src, Quill.sources.SILENT)
				// 设置图片的 alt 属性
				this.quill.formatText(range.index, 1, 'alt', alt, Quill.sources.SILENT)
				// 设置图片的宽度
				this.quill.formatText(range.index, 1, 'width', width, Quill.sources.SILENT)
				// 设置图片的高度
				this.quill.formatText(range.index, 1, 'height', height, Quill.sources.SILENT)
				// 设置图片的 class 属性
				this.quill.formatText(range.index, 1, 'class', extClass, Quill.sources.SILENT)
				// 设置图片的自定义属性
				this.quill.formatText(range.index, 1, 'data-custom', Object.keys(data).map(key => `${key}=${data[key]}`)
					.join('&'), Quill.sources.SILENT)
				// 在图片下面插入一个换行符
				this.quill.insertText(range.index + 1, '\n', Quill.sources.SILENT)
				// 将光标移动到图片下一行
				this.quill.setSelection(range.index + 2, Quill.sources.SILENT)

				// 滚动到可视区域
				this.quill.scrollIntoView()
			},

			insertText({
				text
			}) {
				// 获取当前光标所在位置
				const range = this.quill.getSelection(true)
				// 在当前光标位置插入文本
				this.quill.insertText(range.index, text, Quill.sources.USER)
				// 将光标移动到文本末尾
				this.quill.setSelection(range.index + text.length, 0, Quill.sources.SILENT)
			},

			setContents({
				html,
				delta
			}) {
				if (html) {
					// 如果传入了 HTML，直接设置 innerHTML
					this.quill.root.innerHTML = html;
				} else if (delta) {
					// 保持原有的 delta 设置方式作为备选
					this.quill.setContents(delta, Quill.sources.SILENT);
				}
			},
			// 获取编辑器内容
			getContents({
				success,
				fail,
				complete
			} = {}) {
				let contents
				try {
					// 获取编辑器内容的 HTML
					const html = this.quill.root.innerHTML
					// 获取编辑器内容的纯文本
					const text = this.quill.getText()
					// 获取编辑器内容的 Delta 对象
					const delta = this.quill.getContents()

					contents = {
						html,
						text,
						delta
					}

					// 如果传入了 success 回调函数，则执行该函数并传入编辑器内容
					if (typeof success === "function") {
						success(contents)
					}
				} catch (e) {
					// 如果传入了 fail 回调函数，则执行该函数并传入错误信息
					if (typeof fail === 'function') {
						fail(e)
					}
				}

				// 如果传入了 complete 回调函数，则执行该函数
				if (typeof complete === 'function') complete()

				// 返回一个 Promise 对象，其状态为已解决，并传入编辑器内容
				return Promise.resolve(contents)
			},

			// 清空编辑器内容
			clear() {
				// 将编辑器内容设置为空数组
				this.quill.setContents([])
			},

			// 移除选中文本的格式
			removeFormat() {
				// 获取当前选中文本的范围
				const range = this.quill.getSelection(true)
				// 获取 Parchment 对象
				const parchment = Quill.import('parchment')
				// 如果选中了文本，则移除选中文本的格式
				if (range.length) {
					this.quill.removeFormat(range, Quill.sources.USER)
				} else {
					// 否则，移除当前光标所在位置的格式
					Object.keys(this.quill.getFormat(range)).forEach(key => {
						if (parchment.query(key, parchment.Scope.INLINE)) {
							this.quill.format(key, false)
						}
					})
				}
			},

			// 撤销
			undo() {
				this.quill.history.undo()
			},

			// 重做
			redo() {
				this.quill.history.redo()
			},

			// 失去焦点
			blur() {
				this.quill.blur()
			},

			// 滚动到可视区域
			scrollIntoView() {
				this.quill.scrollIntoView()
			},

			// 在当前光标位置插入一个解锁内容的占位符
			insertUnlockContent() {
				const range = this.quill.getSelection(true)
				this.quill.insertEmbed(range.index, 'unlockContent', Quill.sources.SILENT)
				this.quill.setSelection(range.index + 1, Quill.sources.SILENT)
			},

			insertMediaVideo({
				poster,
				src,
				duration
			}) {
				const range = this.quill.getSelection(true)
				this.quill.insertEmbed(range.index, 'mediaVideo', {
					poster,
					src,
					duration
				}, Quill.sources.SILENT)
				this.quill.setSelection(range.index + 1, Quill.sources.SILENT)
			},

			// 获取选中文本的纯文本
			getSelectionText() {
				const range = this.quill.selection.savedRange
				const res = {
					text: ''
				}
				if (range && range.length !== 0) {
					res.text = this.quill.getText(range.index, range.length)
				}
				return res
			},

			// 加载 Quill 和 Quill-image-resize-mp 库
			// _loadQuill(callback) {
			//   // 加载本地 quill.js
			// 	// const quillSrc = 'https://unpkg.com/quill@1.3.7/dist/quill.min.js'
			// 	const imageResizeSrc = 'https://unpkg.com/quill-image-resize-mp@3.0.1/image-resize.min.js'
			//
			// 	// loadScript(window.Quill, quillSrc, () => {
			//     loadScript(window.ImageResize, imageResizeSrc, () => {
			//       callback()
			//     })
			// 	// })
			// },

			// 添加 Quill 事件监听器
			addQuillListeners() {
				// 监听光标位置变化事件
				this.quill.on(Quill.events.SELECTION_CHANGE, (range) => {
					this.emitStatusChange(range)

					if (range) {
						this.$emit('focus')
					} else {
						this.$emit('blur')
					}
				})
				// 监听滚动事件
				this.quill.on(Quill.events.SCROLL_OPTIMIZE, () => {
					const range = this.quill.selection.getRange()[0]
					this.emitStatusChange(range)
				})
				// 监听文本变化事件
				this.quill.on(Quill.events.TEXT_CHANGE, (delta, oldDelta, source) => {
					const text = this.quill.getText().replace(/\n/g, '')
					this.$emit('textchange', {
						detail: text.length
					})
				})
			},

			// 触发状态变化事件
			emitStatusChange(range) {
				this.$emit('statuschange', {
					detail: range ? this.quill.getFormat(range) : {}
				})
			}
		}
	}
</script>

<style scoped>

</style>